#include <check.h>
#include <mstore.h>
#include "../internal.h"
#include "../libmstore/internal.h"


/* Create a fresh instance of mstore */
START_TEST(test_mstore_create)
{
	Error err;
	MStore *mstore;

	mstore = mstore_init(3, &err);

	/* Check the mstore instance itself */
	ck_assert(mstore != NULL);
	ck_assert(mstore->nhead != NULL);
	ck_assert_int_eq(mstore->min, 3);

	/* Check the head node */
	ck_assert_int_eq(mstore->nhead->keys, 0);
	ck_assert(mstore->nhead->khead == NULL);
} END_TEST

/* Make sure that creation fails with too few keys */
START_TEST(test_mstore_create_fail1)
{
	Error err;
	MStore *mstore;

	mstore = NULL;
	mstore = mstore_init(0, &err);
	ck_assert(mstore == NULL);


	mstore = mstore_init(1, &err);
	ck_assert(mstore == NULL);

} END_TEST

/* Make sure to fail with invalid error object right away */
START_TEST(test_mstore_create_fail2)
{
	MStore *mstore;

	mstore = mstore_init(3, NULL);
	ck_assert(mstore == NULL);
} END_TEST

/* Insert a single object into an mstore */
START_TEST(test_mstore_insert)
{
	Error err;
	int test;
	MStore *mstore;
	int ret;

	test = 42;
	ret = 0;

	mstore = mstore_init(3, &err);
	ret = mstore_insert(&test, sizeof(test), mstore, &err);

	ck_assert_int_eq(ret, 0);
	ck_assert_int_eq(mstore->nhead->keys, 1);

} END_TEST

/* Make sure that NULL object insertion fails */
START_TEST(test_mstore_insert_fail1)
{
	Error err;
	MStore *mstore;
	int ret;

	mstore = mstore_init(3, &err);
	ret = mstore_insert(NULL, 10, mstore, &err);

	ck_assert_int_eq(ret, -1);
	ck_assert_int_eq(mstore->nhead->keys, 0);

} END_TEST

/* Make sure that a size of 0 is not accepted */
START_TEST(test_mstore_insert_fail2)
{
	Error err;
	MStore *mstore;
	int val;
	int ret;

	val = 42;
	mstore = mstore_init(3, &err);
	ret = mstore_insert(&val, 0, mstore, &err);

	ck_assert_int_eq(ret, -1);
	ck_assert_int_eq(mstore->nhead->keys, 0);
} END_TEST

/* Make sure that an invalid mstore is not accepted */
START_TEST(test_mstore_insert_fail3)
{
	Error err;
	MStore *mstore;
	int val;
	int ret;

	val = 42;
	mstore = NULL;
	ret = mstore_insert(&val, 0, mstore, &err);

	ck_assert_int_eq(ret, -1);
} END_TEST

/* Single key insertion, just checking consistency */
START_TEST(test_mstore_ptr1)
{
	Error err;
	MStore *mstore;
	int val;
	int ret;

	val = 42;

	mstore = mstore_init(3, &err);
	ret = mstore_insert(&val, sizeof(val), mstore, &err);
	ck_assert_int_eq(ret, 0);

	ck_assert(mstore->nhead->khead != NULL);
	ck_assert(mstore->nhead->khead->rcn == NULL);
	ck_assert(mstore->nhead->khead->lcn == NULL);
	ck_assert(mstore->nhead->khead->next == NULL);
	ck_assert(mstore->nhead->khead->prev == NULL);
	ck_assert(mstore->nhead->khead->node == mstore->nhead);

	MHash mid = mhash(mstore->nhead->khead->data, sizeof(val));
	ck_assert_int_eq(mhashcmp(&mstore->nhead->khead->mid, &mid),  0);
} END_TEST

/* Test the capacity limits of a single node */
START_TEST(test_mstore_cap)
{
	MStore *mstore;
	Error err;
	int *v;
	int ret;

	mstore = mstore_init(3, &err);
	ck_assert(mstore != NULL);

	for(int val=0; val<6; ++val) {
		v = malloc(sizeof(int));
		*v = val;

		ret = mstore_insert(v, sizeof(*v), mstore, &err);
		ck_assert_int_eq(mstore->nhead->keys, val+1);
	}

	v = malloc(sizeof(int));
	*v = 42;

	ret = mstore_insert(v, sizeof(*v), mstore, &err);
	ck_assert_int_eq(ret, 0);
	ck_assert_int_eq(mstore->nhead->keys, 1);

	/*
	 * Root node should have 1 (as an exception),
	 * children *must* have the minimum amount.
	 */
	ck_assert_int_eq(mstore->nhead->khead->lcn->keys, 3);
	ck_assert_int_eq(mstore->nhead->khead->rcn->keys, 3);

} END_TEST

/* Test value order of tree nodes */
START_TEST(test_mstore_order)
{
	MKey *k1, *k2, *kh;
	MStore *mstore;
	Error err;
	int *v;
	int ret;

	k1 = NULL;
	k2 = NULL;

	mstore = mstore_init(3, &err);
	ck_assert(mstore != NULL);

	for(int val=0; val<6; ++val) {
		v = malloc(sizeof(int));
		*v = val;
		ret = mstore_insert(v, sizeof(*v), mstore, &err);
	}

	v = malloc(sizeof(int));
	*v = 42;

	ret = mstore_insert(v, sizeof(*v), mstore, &err);
	
	kh = mstore->nhead->khead;
	k1 = mstore->nhead->khead->lcn->khead;
	k2 = mstore->nhead->khead->rcn->khead;

	ck_assert(keycmp(kh, k1) > 0);
	ck_assert(keycmp(kh, k2) < 0);

	for(MKey *k=k1; k->next != NULL; k = k->next)
		ck_assert(keycmp(k, k->next) < 0);
	for(MKey *k=k2; k->next != NULL; k = k->next)
		ck_assert(keycmp(k, k->next) < 0);

} END_TEST

START_TEST(test_mstore_2prop)
{
	MKey *k1;
	MKey *k2;
	MStore *mstore;
	Error err;
	int *v;
	int ret;

	k1 = NULL;
	k2 = NULL;

	mstore = mstore_init(3, &err);
	ck_assert(mstore != NULL);

	/* Forces second rebalance on next insertion */
	for(int val=0; val<12; ++val) {
		v = malloc(sizeof(int));
		*v = val;
		ret = mstore_insert(v, sizeof(*v), mstore, &err);
	}

	v = malloc(sizeof(int));
	*v = 42;

	/* Going from 0, there should be 3,7 in the top node */
	ret = mstore_insert(v, sizeof(*v), mstore, &err);
	ck_assert_int_eq(ret, 0);
	ck_assert_int_eq(mstore->nhead->keys, 2);

	k1 = mstore->nhead->khead;
	k2 = mstore->nhead->khead->next;

	ck_assert(keycmp(k1, k2) < 0);
	ck_assert(keycmp(k1, k1->lcn->khead) > 0);

} END_TEST

START_TEST(test_mstore_search)
{
	Error err;
	MHash mid;
	MStore *mstore;
	int ret;
	int val;
	int *valptr;

	valptr = NULL;
	val = 42;
	mid = mhash(&val, sizeof(val));

	mstore = mstore_init(3, &err);
	ret = mstore_insert(&val, sizeof(val), mstore, &err);
	ck_assert_int_eq(ret, 0);

	ret = mstore_search((void**)&valptr, mid, mstore, &err);
	ck_assert_int_eq(ret, 0);

	ck_assert_int_eq(*valptr, 42);
	ck_assert(valptr == &val);
	ck_assert_int_eq(mhashcmp(&mid, &mstore->nhead->khead->mid), 0);

} END_TEST

/* Allocate a new key correctly */
START_TEST(test_allockey)
{
	Error err;
	int test;
	MKey *key;

	test = 42;
	key = allockey(&test, sizeof(test), &err);

	ck_assert(key != NULL);
	ck_assert(key->data != NULL);
	ck_assert_int_eq(*(int*)key->data, test);

} END_TEST

Suite*
mstore_suite(void)
{
	Suite *s;
	TCase *tc_mstore_core;

	s = suite_create("MStore");
	tc_mstore_core = tcase_create("mstore_create");

	tcase_add_test(tc_mstore_core, test_mstore_create);
	tcase_add_test(tc_mstore_core, test_mstore_create_fail1);
	tcase_add_test(tc_mstore_core, test_mstore_create_fail2);
	tcase_add_test(tc_mstore_core, test_mstore_insert);
	tcase_add_test(tc_mstore_core, test_mstore_insert_fail1);
	tcase_add_test(tc_mstore_core, test_mstore_insert_fail2);
	tcase_add_test(tc_mstore_core, test_mstore_insert_fail3);
	tcase_add_test(tc_mstore_core, test_mstore_ptr1);
	tcase_add_test(tc_mstore_core, test_mstore_cap);
	tcase_add_test(tc_mstore_core, test_mstore_order);
	tcase_add_test(tc_mstore_core, test_mstore_2prop);
	tcase_add_test(tc_mstore_core, test_mstore_search);
	tcase_add_test(tc_mstore_core, test_allockey);

	suite_add_tcase(s, tc_mstore_core);
	return s;
}

int
main(int argc, char* argv[])
{
	int failed;
	Suite *suite;
	SRunner *sr;

	failed = 0;
	suite = mstore_suite();
	sr = srunner_create(suite);

	srunner_run_all(sr, CK_NORMAL);
	failed = srunner_ntests_failed(sr);

	if(failed != 0)
		fprintf(stderr, "Number of failed tests: %d\n", failed);

	srunner_free(sr);
	return 0;
}
